package com.atguigu.gmall.list.service.impl;

import com.atguigu.gmall.list.service.SkuSearchService;
import com.atguigu.gmall.model.list.*;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.*;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.Aggregations;
import org.elasticsearch.search.aggregations.bucket.nested.NestedAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.mapping.IndexCoordinates;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 钟涛
 * @version 1.0
 * @description: TODO
 * @date 2021/12/21 19:37
 */
@Service
public class SkuSearchServiceImpl implements SkuSearchService {
    @Autowired
    private ElasticsearchRestTemplate esRestTemplate;
    @Override
    public SearchResponseVo search(SearchParam param) {


        /**
         * Query query,
         * Class<T> class,
         * IndexCoordinates index
         */
        Query query = buildQueryDSL(param);
        //2、进行远程ES数据检索
        SearchHits<Goods> hits = esRestTemplate.search(query, Goods.class, IndexCoordinates.of("goods"));
        //3、处理检索到的数据(根据前端的需求进行数据的整理)
        return BuildSearchResponseVo(hits,param);
    }

    /**
     * 根据从es中查询到的数据，返回前端需要的数据模型
     * @param hits
     * @return
     */
    private SearchResponseVo BuildSearchResponseVo(SearchHits<Goods> hits,SearchParam param) {
        SearchResponseVo responseVo = new SearchResponseVo();
        //1.基本数据
        responseVo.setTotal(hits.getTotalHits());
        responseVo.setPageNo(param.getPageNo());
        responseVo.setPageSize(param.getPageSize());
        //设置总页码
        responseVo.setTotalPages(hits.getTotalHits()%param.getPageSize() == 0 ? hits.getTotalHits()/param.getPageSize():(hits.getTotalHits()/param.getPageSize())+1);
        //2、查询到的商品
        List<Goods> goods = new ArrayList<>(hits.getSearchHits().size());
        for (SearchHit<Goods> searchHit : hits.getSearchHits()) {
            Goods good = searchHit.getContent();
            //如果是模糊检索，需要用高亮后的title替换原来title
            if(!StringUtils.isEmpty(param.getKeyword())){
                good.setTitle(searchHit.getHighlightField("title").get(0));
            }
            goods.add(good);
        }
        responseVo.setGoodsList(goods);

        //3、聚合的品牌信息(封装以上商品涉及到的所有品牌)
        List<SearchResponseTmVo> tmVoList = new ArrayList<>();
        ParsedLongTerms tm_agg = hits.getAggregations().get("tm_agg");
        for (Terms.Bucket bucket : tm_agg.getBuckets()) {
            //遍历品牌桶
            long tmID = bucket.getKeyAsNumber().longValue();
            ParsedStringTerms tmname_agg = bucket.getAggregations().get("tmname_agg");
            String tmName = tmname_agg.getBuckets().get(0).getKeyAsString();
            ParsedStringTerms tmlogo_agg = bucket.getAggregations().get("tmlogo_agg");
            String tmLogoUrl = tmlogo_agg.getBuckets().get(0).getKeyAsString();
            SearchResponseTmVo tmVo = new SearchResponseTmVo();
            tmVo.setTmId(tmID);
            tmVo.setTmName(tmName);
            tmVo.setTmLogoUrl(tmLogoUrl);
            tmVoList.add(tmVo);
        }
        responseVo.setTrademarkList(tmVoList);
        //4、聚合的其他平台属性
        List<SearchResponseAttrVo> attrList = new ArrayList<>();
        ParsedNested props_agg = hits.getAggregations().get("props_agg");
        ParsedLongTerms attrid_agg = props_agg.getAggregations().get("attrid_agg");
        for (Terms.Bucket bucket : attrid_agg.getBuckets()) {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            long attrId = bucket.getKeyAsNumber().longValue();
            attrVo.setAttrId(attrId);//设置属性Id
            ParsedStringTerms attrname_agg = bucket.getAggregations().get("attrname_agg");
            String attrName = attrname_agg.getBuckets().get(0).getKeyAsString();
            attrVo.setAttrName(attrName);//设置属性名

            ParsedStringTerms attrval_agg = bucket.getAggregations().get("attrval_agg");
            List<String> valList = attrval_agg.getBuckets().stream()
                    .map((item) -> item.getKeyAsString())
                    .collect(Collectors.toList());

            attrVo.setAttrValueList(valList);////设置属性值的集合
            attrList.add(attrVo);
        }

        responseVo.setAttrsList(attrList);
        return responseVo;
    }

    /**
     * //1、根据前端传来的所有条件，拼装查询的DSL语句
     * @param param
     * @return
     */
    private Query buildQueryDSL(SearchParam param) {
        NativeSearchQueryBuilder builder = new NativeSearchQueryBuilder();
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();


        //根据前端发来的参数，进行条件的装配
        if(param.getCategory3Id()!=null){
            //三级分类的条件拼装 比如 category3Id=61
            boolQuery.must(QueryBuilders.termQuery("category3Id", param.getCategory3Id()));
        }

        if(!StringUtils.isEmpty(param.getKeyword())){
            //全文匹配的条件拼装   比如 keyword=小米
            boolQuery.must(QueryBuilders.matchQuery("title", param.getKeyword()));
        }

        if(!StringUtils.isEmpty(param.getTrademark())){
            //品牌的条件拼装  比如 trademark=1:小米
//            boolQuery.must(QueryBuilders.termQuery("tmId", Long.parseLong(param.getTrademark().split(":")[0])));
          boolQuery.must(QueryBuilders.termQuery("tmId", param.getTrademark().split(":")[0]));
        }

        if(param.getProps()!=null && param.getProps().length > 0){
            //平台属性的条件拼装  比如 props=23:4G:运行内存&props=24:256G:机身内存
            for (String prop : param.getProps()) {
                String[] split = prop.split(":");
                BoolQueryBuilder must = QueryBuilders.boolQuery()
                        .must(QueryBuilders.termQuery("attrs.attrId", split[0]))
                        .must(QueryBuilders.termQuery("attrs.attrValue", split[1]));
                NestedQueryBuilder nestedQuery = QueryBuilders.nestedQuery("attrs", must, ScoreMode.None);
               //每个嵌入式条件拼装完，放到条件里去
                boolQuery.must(nestedQuery);
            }
        }



        //查询条件封装结束
        builder.withQuery(boolQuery);
        //排序开始
        if(!StringUtils.isEmpty(param.getOrder())){
            String[] split = param.getOrder().split(":");
            String field = "hotScore";
            if(split[0].equals("2"))field = "price";
            SortOrder sortOrder = SortOrder.DESC;
            try{
                sortOrder = SortOrder.fromString(split[1]);
            }catch (Exception e){

            }
            FieldSortBuilder fieldSortBuilder = SortBuilders.fieldSort(field).order(sortOrder);

            builder.withSort(fieldSortBuilder);
        }
        //分页开始
        PageRequest pageRequest = PageRequest.of(param.getPageNo()-1, param.getPageSize());
        builder.withPageable(pageRequest);
        if(!StringUtils.isEmpty(param.getKeyword())){
            //高亮开始
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title").preTags("<span style='color:red'>").postTags("</span>");
            builder.withHighlightBuilder(highlightBuilder);

        }

        //聚合开始
           //分析品牌聚合
        TermsAggregationBuilder tmSubAggregation = AggregationBuilders.terms("tm_agg")
                .field("tmId").size(1000)
                .subAggregation(AggregationBuilders.terms("tmname_agg")
                        .field("tmName").size(1000)
                ).subAggregation(AggregationBuilders.terms("tmlogo_agg")
                        .field("tmLogoUrl").size(1000)
                );

        builder.addAggregation(tmSubAggregation);

           //分析属性聚合
        NestedAggregationBuilder propsSub = AggregationBuilders.nested("props_agg", "attrs")
                .subAggregation(
                        AggregationBuilders.terms("attrid_agg").
                                field("attrs.attrId").size(1000)
                                .subAggregation(AggregationBuilders.terms("attrname_agg").field("attrs.attrName").size(1000))
                                .subAggregation(AggregationBuilders.terms("attrval_agg").field("attrs.attrValue").size(1000))
                );

        builder.addAggregation(propsSub);
        //返回Query对象
        return builder.build();
    }
}
